﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using GraphosData;
using System.Windows.Media.Animation;

namespace GraphosControls
{

	// graph viewer constants
	internal static class Const
	{
		public static double EdgeDestroyAnimationTime = 0.5;
		public static double EdgeCreateAnimationTime = 0.5;
		public static double NodeCreateAnimationTime = 0.5;
		public static double NodeDestroyAnimationTime = 0.5;
		public static double DestoryTime = 1.0;
	}
	// easy access random numbers
	internal static class Rand
	{
		public static double Double
		{
			get { return rand.NextDouble(); }
		}
		static Random rand = new Random();
	}

	/// <summary>
	/// Interaction logic for GraphViewer.xaml
	/// </summary>
	public partial class GraphViewer : UserControl
	{

		public GraphViewer()
		{
			InitializeComponent();
		}

		#region Dependency Properties

		public Node SelectedNode
		{
			get { return (Node)GetValue(SelectedNodeProperty); }
			set { SetValue(SelectedNodeProperty, value); }
		}
		public static readonly DependencyProperty SelectedNodeProperty =
			 DependencyProperty.Register("SelectedNode", typeof(Node), typeof(GraphViewer), new UIPropertyMetadata(null, new PropertyChangedCallback(OnSelectedNodeChanged)));
		private static void OnSelectedNodeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
		{
			GraphViewer viewer = sender as GraphViewer;
			viewer.ArrangeNodes();
		}

		public Graph Graph
		{
			get { return (Graph)GetValue(GraphProperty); }
			set { SetValue(GraphProperty, value); }
		}
		public static readonly DependencyProperty GraphProperty =
			 DependencyProperty.Register("Graph", typeof(Graph), typeof(GraphViewer), new UIPropertyMetadata(new Graph()));

		#endregion

		public void RandomNodes()
		{
			foreach (var vn in m_nodeToVisualNode.Values)
				RandomizeNodeLocation(vn);	
		}

		private void VisualNode_Click(object sender, RoutedEventArgs e)
		{
			VisualNode vn = sender as VisualNode;
			if (vn == null)
				return;

			SelectedNode = vn.Node;
		}
		private void VisualNode_Loaded(object sender, RoutedEventArgs e)
		{
			VisualNode vn = sender as VisualNode;
			Canvas.SetLeft(vn, 0.5 * MainCanvas.ActualWidth);
			Canvas.SetTop(vn, 0.5 * MainCanvas.ActualHeight);

			RandomizeNodeLocation(vn);
		}

		private void RandomizeNodeLocation(VisualNode vn)
		{
			PointAnimation anim = new PointAnimation(
				new Point(Rand.Double * MainCanvas.ActualWidth, Rand.Double * MainCanvas.ActualHeight),
				 new Duration(TimeSpan.FromSeconds(1)));
			anim.Freeze();
			vn.BeginAnimation(VisualNode.LocationProperty, anim);
		}

		private void ArrangeNodes()
		{
			VisualNode visualRoot = AddVisualNode(SelectedNode);

			var nodesToRemove = m_nodeToVisualNode.Keys.Where(n => n != SelectedNode).Except(SelectedNode.AdjacentNodes).ToList();
			var nodesToAdd = SelectedNode.AdjacentNodes.Except(m_nodeToVisualNode.Keys);

			foreach (var node in nodesToRemove)
				RemoveVisualEdges(node);

			foreach (var node in nodesToRemove)
				RemoveVisualNode(node);

			foreach (var node in nodesToAdd)
				AddVisualNode(node);

			foreach (var lineNode in m_nodeToEdges.Values)
				foreach (var line in lineNode.Keys)
					MainCanvas.Children.Remove(line);
			m_nodeToEdges.Clear();

			foreach (VisualNode vn in m_nodeToVisualNode.Values)
			{
				vn.AdjacentVisualNodes = 
					from n in vn.Node.AdjacentNodes
					where m_nodeToVisualNode.ContainsKey(n)
					select m_nodeToVisualNode[n];
			}
			foreach (VisualNode from in visualRoot.AdjacentVisualNodes)
			{
				foreach (VisualNode to in from.AdjacentVisualNodes)
				{
					AddSecondaryVisualEdge(from, to);
				}
			}
			//var visualsToAdjacentVisuals =
			//   from n1 in SelectedNode.AdjacentNodes
			//      let n1Adjacents =
			//         from n2 in n1.AdjacentNodes
			//         where SelectedNode.AdjacentNodes.Contains(n2)
			//         select m_nodeToVisualNode[n2]
			//   select new { vn = m_nodeToVisualNode[n1], adjacents = n1Adjacents };

			//// build map for positioning:
			//Dictionary<VisualNode, IEnumerable<VisualNode>> visualNodeToAdjacentVisualNodes = 
			//   new Dictionary<VisualNode, IEnumerable<VisualNode>>();
			//visualNodeToAdjacentVisualNodes[root] = SelectedNode.AdjacentNodes.Select<Node, VisualNode>(n => m_nodeToVisualNode[n]);

			//foreach (var var in visualsToAdjacentVisuals)
			//{
			//   visualNodeToAdjacentVisualNodes[var.vn] = var.adjacents;
			//   foreach (var n in var.adjacents)
			//      AddSecondaryVisualEdge(var.vn, n);
			//}

			//foreach (var pair in allNonRootNodePairs)
			//{
			//   AddSecondaryVisualEdge(pair.node1, pair.node2);
			//}

			m_nodeToEdges[visualRoot] = new Dictionary<VisualEdge, VisualNode>();
			foreach (var node in SelectedNode.AdjacentNodes)
			{
				VisualNode child = m_nodeToVisualNode[node];
				AddPrimaryVisualEdge(visualRoot, child);
			}

			// positionme up
			//MichaelsFunction(visualNodeToAdjacentVisualNodes);

			foreach (var pair in m_nodeToVisualNode)
				pair.Value.IsSelected = pair.Key == SelectedNode;

			MainCanvas.Children.Capacity = MainCanvas.Children.Count;
			Console.WriteLine("MainCanvas: {0}, {1}", MainCanvas.Children.Count, MainCanvas.Children.Capacity);
			Console.WriteLine("Edges: {0}", m_nodeToEdges.Values.Sum(d => d.Count));
			Console.WriteLine("Nodes: {0}", m_nodeToVisualNode.Values.Count);
		}

		private VisualNode AddVisualNode(Node node)
		{
			VisualNode vn;
			if (!m_nodeToVisualNode.TryGetValue(node, out vn))
			{
				vn = new VisualNode(node, MainCanvas);
				//vn.Style = (Style)TryFindResource("VisualNodeStyle");
				m_nodeToVisualNode.Add(node, vn);
			}
			return vn;
		}
		private VisualEdge AddPrimaryVisualEdge(VisualNode start, VisualNode end)
		{
			if (!m_nodeToEdges.ContainsKey(start))
				m_nodeToEdges[start] = new Dictionary<VisualEdge, VisualNode>();
			VisualEdge edge = new VisualEdge(start, end, MainCanvas);
			edge.Style = (Style)TryFindResource("PrimaryVisualEdgeStyle");
			m_nodeToEdges[start][edge] = end;
			return edge;
		}
		private VisualEdge AddSecondaryVisualEdge(VisualNode start, VisualNode end)
		{
			if (!m_nodeToEdges.ContainsKey(start))
				m_nodeToEdges[start] = new Dictionary<VisualEdge, VisualNode>();
			VisualEdge edge = new VisualEdge(start, end, MainCanvas);
			edge.Style = (Style)TryFindResource("SecondaryVisualEdgeStyle");
			m_nodeToEdges[start][edge] = end;
			return edge;
		}
		private void RemoveVisualNode(Node node)
		{
			VisualNode vn;
			if (m_nodeToVisualNode.TryGetValue(node, out vn))
			{
				m_nodeToVisualNode.Remove(node);
				vn.DestroyWithAnimation();
			}
		}

		/// <summary>
		/// Removes a visual edge from the visual tree
		/// </summary>
		/// <param name="node">the node key to remove</param>
		private void RemoveVisualEdges(Node node)
		{
			var vn = m_nodeToVisualNode[node];
			Dictionary<VisualEdge, VisualNode> edgeToNode;
			if (m_nodeToEdges.TryGetValue(vn, out edgeToNode))
			{
				foreach (var edge in edgeToNode.Keys)
				{
					edge.DestroyWithAnimatin();
				}
				m_nodeToEdges.Remove(vn);
			}
		}

		private Dictionary<Node, VisualNode> m_nodeToVisualNode = new Dictionary<Node, VisualNode>();
		private Dictionary<VisualNode, Dictionary<VisualEdge, VisualNode>> m_nodeToEdges = new Dictionary<VisualNode, Dictionary<VisualEdge, VisualNode>>();
	}
}
